#include "httpresponce.h"
const std::unordered_map<std::string,std::string> Httpresponce::SUFFIX_TYPE
{
    {".html","text/html"},
    {".txt","text/plain"},
    {".xml","text/xml"}
};
const std::unordered_map<int,std::string> Httpresponce::CODE_STATUS
{
    {200,"OK"},
    {400,"Bad Request"},
    {403,"Forbidden"},
    {404,"Not Found"}
};
const std::unordered_map<int,std::string> Httpresponce::CODE_PATH
{
    {400,"400.html"},
    {403,"403.html"},
    {404,"404.html"}
};
Httpresponce::Httpresponce()
{
    StatusCode_ = -1;
    isKeepAlive_ = false;
    path_ = SrcDir_ ="";
    mmFile_ =nullptr;
    bzero(&mmFileStat_,sizeof(mmFileStat_));
}
Httpresponce::~Httpresponce()
{
    UnmapFile_();
}
void Httpresponce::Init(const std::string& srcdir,std::string& path,bool keepalive,int code)
{
    assert(srcdir !="");
    //在使用第二次 Init 时,重新设置 mmFile_ 在文件域上的
    if(mmFile_==nullptr) {   UnmapFile_();    }
    SrcDir_ = srcdir;
    path_ = path;
    isKeepAlive_ = keepalive;
    StatusCode_=code;
    mmFile_ = nullptr;
    bzero(&mmFileStat_,sizeof(mmFileStat_));
}
void Httpresponce::MakeResponce(Buffer_::Buffer& buff)
{
    if(stat((SrcDir_ + path_).data(),&mmFileStat_)<0 || S_ISDIR(mmFileStat_.st_mode))
    {//文件不存在
        StatusCode_ = 404;
    }
    else if(!(mmFileStat_.st_mode & S_IROTH))
    {//403 的主责在服务端
        StatusCode_ = 403;
    }
    else if(StatusCode_ == -1 )
    {
        StatusCode_ = 200;
    }
    //先判断 是不是错误代码
    SetHtml_();
    //再根据响应的代码生成 响应行
    AddStatLine_(buff);
    AddHead_(buff);
    AddContent_(buff);
}
void Httpresponce::AddStatLine_(Buffer_::Buffer& buff)
{
    std::string status;
    if(CODE_STATUS.count(StatusCode_)==1)
    {
        status = CODE_STATUS.find(StatusCode_)->second;
    }
    else
    {//出现了 已设置错误码范围外的错误码 默认处理为400
        StatusCode_ = 400;
        status = CODE_STATUS.find(StatusCode_)->second;
    }
    buff.append("HTTP/1.1 " + std::to_string(StatusCode_) +" " + status + "\r\n");
}
void Httpresponce::AddHead_(Buffer_::Buffer& buff)
{
    buff.append("Coonection: ");
    if(isKeepAlive_ == false)
    {
        buff.append("close\r\n");
    }
    buff.append("keep-alive\r\n");
    buff.append("keep-alive: max 1024, timeout=120\r\n");
    buff.append("Content-Type: " + GetFileType_() + "\r\n");
}
void Httpresponce::AddContent_(Buffer_::Buffer& buff)
{
    //再次检测文件是否存在并且可读
    //这里会出现服务器中文件存在,但在服务器的代码中没有添加 文件 的名字
    int SrcFd = open((SrcDir_ + path_).data(),O_RDONLY);
    if(SrcFd < 0)
    {
        ErronContent(buff,"File NotFound!");
        return ;
    }

    //建立char* mmFile 对 mmap 的映射
    LOG_DEBUG("file path %s",(SrcDir_ + path_).data());
    int *mmRet = (int*)mmap(0,mmFileStat_.st_size,PROT_READ,MAP_PRIVATE,SrcFd,0);
    if(mmRet == nullptr)
    {//mmap 映射失败
        ErronContent(buff,"File NotFound!");
        return ;
    }
    //拿到 指定文件的内存
    mmFile_ = (char*)mmRet;
    close(SrcFd);
    buff.append(std::string("Content-Length: " + std::to_string(mmFileStat_.st_size) + "\r\n\r\n"));
}
void Httpresponce::SetHtml_()
{
    if(CODE_PATH.count(StatusCode_) == 1)
    {
        path_ = CODE_PATH.find(StatusCode_)->second;
        stat((SrcDir_ + path_).data(),&mmFileStat_); 
    }
}
void Httpresponce::UnmapFile_()
{
    //解除 mmFile_ 在对应文件域上的
    if(mmFile_)
    {
        munmap(mmFile_,mmFileStat_.st_size);
        mmFile_ = nullptr;
    }
}
char* Httpresponce::GetFile_()
{
    return mmFile_;
}
size_t Httpresponce::GetfileLen_()
{
    return mmFileStat_.st_size;
}
std::string Httpresponce::GetFileType_()
{
    std::string::size_type index = path_.find_last_of(".");
    if(index == std::string::npos)
    {
        return "text/plain";
    }
    std::string suffix = path_.substr(index);
    if(SUFFIX_TYPE.count(suffix)==1)
    {
        return SUFFIX_TYPE.find(suffix)->second;
    }
    return "text/plain";
}
void Httpresponce::ErronContent(Buffer_::Buffer& buff,std::string message)
{
    //生成临时的 html
    std::string body;
    std::string status;
    body += "<html><title>Error</title>";
    body += "<body bgcolor=\"ffffff\">";
    if(CODE_STATUS.count(StatusCode_) == 1) {
        status = CODE_STATUS.find(StatusCode_)->second;
    } else {
        status = "Bad Request";
    }
    body += std::to_string(StatusCode_) + " : " + status  + "\n";
    body += "<p>" + message + "</p>";
    body += "<hr><em>TinyWebServer</em></body></html>";

    buff.append("Content-length: " + std::to_string(body.size()) + "\r\n\r\n");
    buff.append(body);             
}